﻿using System;
using System.Web;
using System.Web.Routing;
using System.Diagnostics;
using System.Security.Permissions;

using Fuse8.ContentManagementFramework.Web.Caching;
using Fuse8.DomainFramework.Common.Locator;

namespace Fuse8.ContentManagementFramework.Web.ApplicationRuntime
{
    [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Medium)]
    [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Medium)]
    public class CmsModule : IHttpModule
    {
        private static object _cmsRouteKey = new object();

        private HttpApplication _application;
        private Stopwatch _timer;
        private CmsOutputCacheTracker _trackerCapturingResponse;

        #region IHttpModule Members

        public void Dispose()
        {
            _timer = null;
        }

        public void Init(HttpApplication httpApplication)
        {
            _timer = new Stopwatch();

            _application = httpApplication;

            _application.BeginRequest += new EventHandler(Application_BeginRequest);
            _application.ResolveRequestCache += new EventHandler(Application_ResolveRequestCache);
            _application.PostMapRequestHandler += new EventHandler(Application_PostMapRequestHandler);
            _application.UpdateRequestCache += new EventHandler(Application_UpdateRequestCache);
            _application.PreSendRequestHeaders += new EventHandler(Application_PreSendRequestHeaders);
            _application.EndRequest += new EventHandler(Application_EndRequest);
        }

        private void Application_BeginRequest(object sender, EventArgs e)
        {
            BeginRequest(new HttpContextWrapper(_application.Context));
        }

        private void Application_ResolveRequestCache(object sender, EventArgs e)
        {
            ResolveRequestCache(new HttpContextWrapper(_application.Context));
        }

        private void Application_PostMapRequestHandler(object sender, EventArgs e)
        {
            PostMapRequestHandler(new HttpContextWrapper(_application.Context));
        }

        private void Application_UpdateRequestCache(object sender, EventArgs e)
        {
            UpdateRequestCache(new HttpContextWrapper(_application.Context));
        }

        private void Application_PreSendRequestHeaders(object sender, EventArgs e)
        {
            PreSendRequestHeaders(new HttpContextWrapper(_application.Context));
        }

        private void Application_EndRequest(object sender, EventArgs e)
        {
            EndRequest(new HttpContextWrapper(_application.Context));
        }

        #endregion

        #region Event handlers

        protected virtual void BeginRequest(HttpContextBase context)
        {
            _timer.Reset();
            _timer.Start();
        }

        protected virtual void ResolveRequestCache(HttpContextBase context)
        {
            var routeData = CmsRoute.GetRouteData(context); //TODO: if some exception was happened need to show something like - hey they, here is exception
            if (routeData != null)
            {
                if (routeData.DataTokens.ContainsKey(CmsRequestData.UseFileCacheKey) &&
                    (bool)routeData.DataTokens[CmsRequestData.UseFileCacheKey])
                {
                    var currentTracker = CmsOutputCacheManager.Lookup(context);

                    if (currentTracker != null)
                    {
                        if (currentTracker.TrySendResponseOrStartResponseCapture(new HttpResponseWrapper(_application.Response)))
                        {
                            AddCmsHeaders(context);

                            _application.CompleteRequest();
                        }
                        else
                        {
                            _trackerCapturingResponse = currentTracker;
                        }
                    }
                }
                else
                {
                    var routeHandler = routeData.RouteHandler;
                    if (!(routeHandler is StopRoutingHandler))
                    {
                        var requestContext = new RequestContext(context, routeData);
                        var httpHandler = routeHandler.GetHttpHandler(requestContext);
                        if (httpHandler != null)
                        {
                            var data = new CmsRequestData
                            {
                                OriginalPath = context.Request.Path,
                                HttpHandler = httpHandler
                            };

                            context.Items[_cmsRouteKey] = data;
                            context.RewritePath("~/UrlRouting.axd");
                        }
                    }
                }
            }
            else
            {
                if (context.Request.RawUrl.Equals("/admin/", StringComparison.OrdinalIgnoreCase))
                {
                    context.RewritePath("/admin/cmsadminhome");
                }
            }
        }

        protected virtual void PostMapRequestHandler(HttpContextBase context)
        {
            var requestData = context.Items[_cmsRouteKey] as CmsRequestData;
            if (requestData != null)
            {
                context.RewritePath(requestData.OriginalPath);
                context.Handler = requestData.HttpHandler;
            }
        }

        protected virtual void UpdateRequestCache(HttpContextBase context)
        {
            if (_trackerCapturingResponse != null)
            {
                if (_application.Context.Error != null)
                {
                    //no needed to cache errors
                    _trackerCapturingResponse.CancelCapture(new HttpResponseWrapper(_application.Response));
                }
                else
                {
                    _trackerCapturingResponse.FinishCaptureAndSaveResponse(new HttpResponseWrapper(_application.Response));
                }

                _trackerCapturingResponse = null;
            }
        }

        protected virtual void PreSendRequestHeaders(HttpContextBase context)
        {
            AddCmsHeaders(context);
        }

        protected virtual void EndRequest(HttpContextBase context)
        {
            if (_trackerCapturingResponse != null)
            {
                try
                {
                    _trackerCapturingResponse.CancelCapture(new HttpResponseWrapper(_application.Response));
                }
                finally
                {
                    _trackerCapturingResponse = null;
                }
            }
        }

        #endregion

        #region Add CMS information

        protected virtual void AddCmsHeaders(HttpContextBase context)
        {
            if (_timer != null)
            {
                _timer.Stop();

                _application.Response.AddHeader("cms.render",
                    string.Format("{0} s", Math.Round(((decimal)_timer.ElapsedMilliseconds) / 1000, 3, MidpointRounding.AwayFromZero)));
            }
        }

        #endregion
    }
}
